package org.budo.support.lang.util;

import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author lmw
 */
public class NumberUtil {
    private static final Logger log = Slf4j.getLogger();

    public static Boolean equals(Object num1, Number num2) {
        if (null == num1 || null == num2) {
            return false;
        }

        if (num1 instanceof Number) {
            return ((Number) num1).longValue() == num2.longValue();
        }

        if (num1 instanceof String) {
            Long long1 = toLong(num1);
            return equals(long1, num2);
        }

        return false;
    }

    public static Boolean equals(Number num1, Number num2) {
        if (null == num1 || null == num2) {
            return false;
        }

        return num1.longValue() == num2.longValue();
    }

    public static List<Long> toLongList(String in) {
        if (StringUtil.isEmpty(in)) {
            return new ArrayList<Long>();
        }

        if (in.startsWith("[") && in.endsWith("]")) {
            in = in.substring(1, in.length() - 1);
        }

        String[] attar = in.split(",");
        List<Long> out = new ArrayList<Long>();
        for (int i = 0; i < attar.length; i++) {
            String each = attar[i];
            if (NumberUtil.isLong(each)) {
                out.add(toLong(each));
            } else {
                log.error("#37 toIntegerArray error, intArrayString=" + in);
            }
        }
        return out;
    }

    public static List<Integer> toIntegerList(String in) {
        Integer[] array = toIntegerArray(in);
        return null == array ? null : Arrays.asList(array);
    }

    /**
     * 将逗号分隔的字符串转为Integer数组
     *
     * @param in
     */
    public static Integer[] toIntegerArray(String in) {
        if (StringUtil.isEmpty(in)) {
            return new Integer[0];
        }

        String[] array = in.split(",");
        Integer[] out = new Integer[array.length];
        for (int i = 0; i < array.length; i++) {
            String each = array[i];
            if (NumberUtil.isInteger(each)) {
                out[i] = NumberUtil.toInteger(each);
            } else {
                log.error("#37 toIntegerArray error, intArrayString=" + in);
            }
        }
        return out;
    }

    public static List<Integer> toIntegerList(List list) {
        Integer[] array = toIntegerArray(list);
        return ListUtil.arrayToList(array);
    }

    public static Integer[] toIntegerArray(List list) {
        if (null == list) {
            return null;
        }

        Integer[] array = new Integer[list.size()];
        for (int i = 0; i < array.length; i++) {
            Object each = list.get(i);
            array[i] = toInteger(each);
        }

        return array;
    }

    public static Integer[] toIntegerArray(Object[] in) {
        if (null == in) {
            return null;
        }

        Integer[] out = new Integer[in.length];
        for (int i = 0; i < out.length; i++) {
            Object each = in[i];
            out[i] = toInteger(each);
        }

        return out;
    }

    public static Boolean isInteger(Object value) {
        if (null == value) {
            return false;
        }

        if (value instanceof Integer) {
            return true;
        }

        String string = value.toString().trim();
        if (string.isEmpty()) {
            return false;
        }

        int len = string.length();
        for (int i = 0; i < len; i++) {
            if (!Character.isDigit(string.codePointAt(i))) {
                return false;
            }
        }

        return true;
    }

    public static boolean isDouble(Object value) {
        if (null == value) {
            return false;
        }

        if (value instanceof Double) {
            return true;
        }

        try {
            Double.parseDouble(value.toString());
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * 可以被转为long
     */
    public static boolean isLong(Object value) {
        if (null == value) {
            return false;
        }

        if (value instanceof Long) {
            return true;
        }

        try {
            Long.parseLong(value.toString().trim());
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    public static Long toLong(Object value, Number defaultValue) {
        Long defLong = null == defaultValue ? null : defaultValue.longValue();
        if (null == value) {
            return defLong;
        }

        if (value instanceof Number) {
            return ((Number) value).longValue();
        }

        if (value instanceof String) {
            if (((String) value).trim().isEmpty() //
                    || "null".equalsIgnoreCase(((String) value).trim()) //
                    || "undefined".equalsIgnoreCase(((String) value).trim())) {
                return defLong;
            }

            return new Long(((String) value).trim());
        }

        if (value instanceof byte[]) {
            if (((byte[]) value).length < 1) {
                return defLong;
            }

            return new Long(new String((byte[]) value).trim());
        }

        String message = "#97 must input number or string, value=" + value + ", type=" + value.getClass() + ", defaultValue=" + defaultValue;
        throw new IllegalArgumentException(message);
    }

    public static Integer toInteger(Object value, Integer defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        if (value instanceof Number) {
            return ((Number) value).intValue();
        }

        if (value instanceof String) {
            if (((String) value).trim().isEmpty() //
                    || "null".equalsIgnoreCase(((String) value).trim()) //
                    || "undefined".equalsIgnoreCase(((String) value).trim())) {
                return defaultValue;
            }

            return new Integer(((String) value).trim());
        }

        if (value instanceof byte[]) {
            if (((byte[]) value).length < 1) {
                return defaultValue;
            }

            return new Integer(new String((byte[]) value).trim());
        }

        String message = "#126 must input number or string, value=" + value + ", type=" + value.getClass() + ", defaultValue=" + defaultValue;
        throw new IllegalArgumentException(message);
    }

    public static Double toDouble(Object value, Double defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }

        if (value instanceof String) {
            if (((String) value).trim().isEmpty()) {
                return defaultValue;
            }

            return new Double(((String) value).trim());
        }

        if (value instanceof byte[]) {
            if (((byte[]) value).length < 1) {
                return defaultValue;
            }

            return new Double(new String((byte[]) value).trim());
        }

        String message = "#155 must input number or string, value=" + value + ", type=" + value.getClass() + ", defaultValue=" + defaultValue;
        throw new IllegalArgumentException(message);
    }

    public static Short toShort(Object value, Short defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        if (value instanceof Number) {
            return new Short(((Number) value).shortValue());
        }

        if (value instanceof String) {
            if (((String) value).trim().isEmpty()) {
                return defaultValue;
            }

            return new Short(((String) value).trim());
        }

        if (value instanceof byte[]) {
            if (((byte[]) value).length < 1) {
                return defaultValue;
            }

            return new Short(new String((byte[]) value).trim());
        }

        String message = "#184 must input number or string, value=" + value + ", type=" + value.getClass() + ", defaultValue=" + defaultValue;
        throw new IllegalArgumentException(message);
    }

    public static Float toFloat(Object value, Float defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        if (value instanceof Number) {
            return new Float(((Number) value).floatValue());
        }

        if (value instanceof String) {
            if (((String) value).trim().isEmpty()) {
                return defaultValue;
            }

            return new Float(((String) value).trim());
        }

        if (value instanceof byte[]) {
            if (((byte[]) value).length < 1) {
                return defaultValue;
            }

            return new Float(new String((byte[]) value).trim());
        }

        String message = "#213 must input number or string, value=" + value + ", type=" + value.getClass() + ", defaultValue=" + defaultValue;
        throw new IllegalArgumentException(message);
    }

    public static Byte toByte(Object value, Byte defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        if (value instanceof Number) {
            return new Byte(((Number) value).byteValue());
        }

        if (value instanceof String) {
            if (((String) value).trim().isEmpty()) {
                return defaultValue;
            }

            return new Byte((String) value);
        }

        if (value instanceof byte[]) {
            if (((byte[]) value).length < 1) {
                return defaultValue;
            }

            return new Byte(new String((byte[]) value));
        }

        String message = "#242 must input number or string, value=" + value + ", type=" + value.getClass() + ", defaultValue=" + defaultValue;
        throw new IllegalArgumentException(message);
    }

    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        if (value instanceof String || value instanceof Number) {
            if (value.toString().trim().isEmpty()) {
                return defaultValue;
            }

            return new BigDecimal(value.toString().trim());
        }

        if (value instanceof byte[]) {
            if (((byte[]) value).length < 1) {
                return defaultValue;
            }

            return new BigDecimal(new String((byte[]) value).trim());
        }

        String message = "#267 must input number or string, value=" + value + ", type=" + value.getClass() + ", defaultValue=" + defaultValue;
        throw new IllegalArgumentException(message);
    }

    public static BigInteger toBigInteger(Object value, BigInteger defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        if (value instanceof String || value instanceof Number) {
            if (((String) value).trim().isEmpty()) {
                return defaultValue;
            }

            return new BigInteger(value.toString().trim());
        }

        if (value instanceof byte[]) {
            if (((byte[]) value).length < 1) {
                return defaultValue;
            }

            return new BigInteger(new String((byte[]) value).trim());
        }

        String message = "#292 must input number or string, value=" + value + ", type=" + value.getClass() + ", defaultValue=" + defaultValue;
        throw new IllegalArgumentException(message);
    }

    public static AtomicLong toAtomicLong(Object value, AtomicLong defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        Long longValue = toLong(defaultValue);
        return new AtomicLong(longValue);
    }

    public static AtomicInteger toAtomicInteger(Object value, AtomicInteger defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        Integer integerValue = toInteger(value);
        return new AtomicInteger(integerValue);
    }

    public static Integer toInteger(Object value) {
        return toInteger(value, null);
    }

    public static Long toLong(Object value) {
        return toLong(value, null);
    }

    public static BigDecimal toBigDecimal(Object value) {
        return toBigDecimal(value, null);
    }

    public static Double toDouble(Object value) {
        return toDouble(value, null);
    }

    public static Short toShort(Object value) {
        return toShort(value, null);
    }

    public static Float toFloat(Object value) {
        return toFloat(value, null);
    }

    public static Byte toByte(Object value) {
        return toByte(value, null);
    }

    public static BigInteger toBigInteger(Object value) {
        return toBigInteger(value, null);
    }

    public static AtomicLong toAtomicLong(Object value) {
        return toAtomicLong(value, null);
    }

    public static AtomicInteger toAtomicInteger(Object value) {
        return toAtomicInteger(value, null);
    }

    /**
     * 将传入的值转换为指定类型
     *
     * @param value
     * @param type
     */
    public static <T extends Number> T toNumber(Object value, Class<?> type) {
        if (null == value || null == type || value.getClass().equals(type)) {
            return (T) value;
        }

        if (type.equals(Integer.class) || type.equals(int.class)) {
            return (T) toInteger(value);
        }

        if (type.equals(Long.class) || type.equals(long.class)) {
            return (T) toLong(value);
        }

        if (type.equals(Double.class) || type.equals(double.class)) {
            return (T) toDouble(value);
        }

        if (type.equals(Byte.class) || type.equals(byte.class)) {
            return (T) toByte(value);
        }

        if (type.equals(Float.class) || type.equals(float.class)) {
            return (T) toFloat(value);
        }

        if (type.equals(Short.class) || type.equals(short.class)) {
            return (T) toShort(value);
        }

        if (type.equals(BigDecimal.class)) {
            return (T) toBigDecimal(value);
        }

        if (type.equals(BigInteger.class)) {
            return (T) toBigInteger(value);
        }

        if (type.equals(AtomicInteger.class)) {
            return (T) toAtomicInteger(value);
        }

        if (type.equals(AtomicLong.class)) {
            return (T) toAtomicLong(value);
        }

        String message = "#399 value=" + value + ", value.type=" + (null == value ? null : value.getClass()) + ", type=" + type;
        throw new IllegalArgumentException(message);
    }

    /**
     * 判断是否全部小于指定值
     */
    public static Boolean allLessThan(Collection<Integer> values, Integer max) {
        if (null == values || null == max) {
            return null;
        }

        for (Integer each : values) {
            if (each >= max) {
                return false;
            }
        }

        return true;
    }

    public static String toEnglish(String digits) {
        if ("0".equals(digits)) {
            return "zero";
        }

        if ("1".equals(digits)) {
            return "one";
        }

        if ("2".equals(digits)) {
            return "two";
        }

        if ("3".equals(digits)) {
            return "three";
        }

        if ("4".equals(digits)) {
            return "four";
        }

        if ("5".equals(digits)) {
            return "five";
        }

        if ("6".equals(digits)) {
            return "six";
        }

        if ("7".equals(digits)) {
            return "seven";
        }

        if ("8".equals(digits)) {
            return "eight";
        }

        if ("9".equals(digits)) {
            return "nine";
        }

        log.error("#489 toEnglish, digits=" + digits);
        return digits;
    }

    public static String fenToYuan(Integer fen) {
        if (null == fen) {
            return null;
        }

        NumberFormat format = NumberFormat.getInstance();
        format.setGroupingUsed(false);
        format.setMaximumFractionDigits(2);
        return format.format(fen / 100.0);
    }

    public static String sum(Stream<?> stream) {
        if (null == stream) {
            log.error("#601 sum, stream=" + stream);
            return null;
        }

        List<?> list = stream.collect(Collectors.toList());
        return sum(list);
    }

    private static String sum(List<?> list) {
        if (null == list) {
            log.error("#612 sum, list=" + list);
            return null;
        }

        BigDecimal sum = new BigDecimal("0.00");
        for (Object each : list) {
            BigDecimal augend = new BigDecimal(each + "");
            sum = sum.add(augend);
        }

        return sum.toPlainString();
    }

    // 针对 BigDecimal compareTo 仅比较值 不比较 进度 而 equals 既比较值也比较精度
    public static Boolean equals(Object a, Object b) {
        if (null != a && null != b && a.getClass().equals(b.getClass()) && a instanceof Comparable) {
            return 0 == ((Comparable) a).compareTo(b);
        }

        return Objects.equals(a, b);
    }

    public static Boolean greaterThanZero(BigDecimal a) {
        return greaterThan(a, 0);
    }

    public static Boolean greaterThan(BigDecimal a, Number b) {
        if (null == a || null == b) {
            return false;
        }

        return a.compareTo(new BigDecimal(b + "")) > 0;
    }

    public static boolean lessThan(BigDecimal a, BigDecimal b) {
        if (null == a || null == b) {
            return false;
        }

        return a.compareTo(new BigDecimal(b + "")) < 0;
    }
}
